#include "temu.h"
#include "monitor/my_func.h"
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
/* We use the POSIX regex functions to process regular expressions.
 * Type 'man regex' for more information about POSIX regex functions.
 */
#include <sys/types.h>
#include <regex.h>

enum {
	NOTYPE = 256, EQ=254,NUM=255,NQ=253,OX=252,NEG=251,DEREF=250

	/* TODO: Add more token types */

};

static struct rule {
	char *regex;
	int token_type;
	int pri;
} rules[] = {

	/* TODO: Add more rules.
	* Pay attention to the precedence level of different rules.
	*/
	
	{" +",	NOTYPE},	
	{"0[Xx][0-9a-zA-Z]+", OX},			// spaces
	{"[0-9]+", NUM},
	{"\\(", '('},
	{"\\|\\|", '|'},
	{"/", '/'},
	{"\\-", '-'},
	{"\\+", '+'},
	{"\\*", '*'},
	{"\\)", ')'},					// plus
	{"==", EQ},                                  //equal
	{"!=", NQ},
	{"!", '!'},
	{"&&", '&'},
	{"\\$[A-Za-z]+", '$'}
};

#define NR_REGEX (sizeof(rules) / sizeof(rules[0]) )

static regex_t re[NR_REGEX];

/* Rules are used for many times.
 * Therefore we compile them only once before any usage.
 */
void init_regex() {
	int i;
	char error_msg[128];
	int ret;

	for(i = 0; i < NR_REGEX; i ++) {
		ret = regcomp(&re[i], rules[i].regex, REG_EXTENDED);
		if(ret != 0) {
			regerror(ret, &re[i], error_msg, 128);
			Assert(ret == 0, "regex compilation failed: %s\n%s", error_msg, rules[i].regex);
		}
	}
}
static void change_to_lower(char s[])
{
	int len = strlen(s);
	int i=0;
	for(i=0; i<len; i++)
	{
		s[i] = s[i] | 0b00100000;
	}
}

static bool cmp_str(const char* s1, char* s2, int len)
{
	int i;
	for(i=0; i<len; i++)
	{
		if(s1[i] != s2[i])
		{
			return false;
		}
	}
	return true;
}

typedef struct token {
	int type;
	char str[32];
	int pri;
} Token;

Token tokens[32];
int nr_token;

static bool make_token(char *e) {
	int position = 0;
	int i;

	uint32_t address = 0;

	regmatch_t pmatch;
	
	nr_token = 0;

	while(e[position] != '\0') {
		/* Try all rules one by one. */
		for(i = 0; i < NR_REGEX; i ++) {
			if(regexec(&re[i], e + position, 1, &pmatch, 0) == 0 && pmatch.rm_so == 0) {
				char *substr_start = e + position;
				int substr_len = pmatch.rm_eo;

				Log("match rules[%d] = \"%s\" at position %d with len %d: %.*s", i, rules[i].regex, position, substr_len, substr_len, substr_start);
				position += substr_len;

				/* TODO: Now a new token is recognized with rules[i]. Add codes
				 * to record the token in the array `tokens'. For certain types
				 * of tokens, some extra actions should be performed.
				 */

				switch(rules[i].token_type) {
					case NOTYPE: break;
					case '+':  tokens[nr_token++].type = '+';  break;
					case '-': 
					    if(tokens[nr_token-1].type != NUM&& tokens[nr_token-1].type!= ')' && position != 0)
					    {
						    tokens[nr_token++].type = NEG;
					    }
					    else
					    {
						    tokens[nr_token++].type = '-';
					    }
					    break;
					case '*': 
						if(tokens[nr_token-1].type != NUM &&tokens[nr_token-1].type != ')' && position != 0)
						{
							tokens[nr_token++].type = DEREF;
						}
						else
						{
							tokens[nr_token++].type = '*';
						}
						
						break;
					case '/': tokens[nr_token++].type = '/'; break;
					case NUM: 
					    assert(substr_len <= 31);
					    tokens[nr_token].type = NUM; 
					    strncpy(tokens[nr_token].str, substr_start, substr_len);
					    tokens[nr_token].str[substr_len] = '\0';
					    nr_token++;
					    break;
					case '(': tokens[nr_token++].type = '(';break;
					case ')' : tokens[nr_token++].type = ')'; break;
					case NQ: tokens[nr_token++].type = NQ;break;
					case EQ: tokens[nr_token++].type = EQ;break;
					case '!': tokens[nr_token++].type = '!';break;
					case '$': tokens[nr_token].type = NUM;
						assert(substr_len <= 33);
						char tempstr[50];
						strncpy(tempstr, substr_start, substr_len);
						change_to_lower(tempstr);
						int really_search = 0;
						int i=0;
						for(i=0; i<32; i++)
						{
							if(cmp_str(regfile[i], tempstr, substr_len))
							{
								address = cpu.gpr[i]._32;
								really_search = 1;
								break;
							}
						}
						if(cmp_str("$pc", tempstr, substr_len))
						{
							address = cpu.pc;
							really_search = 1;
						}
						if(cmp_str("$hi", tempstr, substr_len))
						{
							address = cpu.hi;
							really_search = 1;
						}
						if(cmp_str("$lo", tempstr, substr_len))
						{
							address = cpu.lo;
							really_search = 1;
						}
						if(really_search == 1)
						{
							sprintf(tokens[nr_token].str, "%u", address);
							nr_token++;
						}
						else
						{
							printf("don't have this register\n");
							return false;
						}
						break;
					case OX: tokens[nr_token].type = NUM; // must change to 10 radix
						assert(substr_len <= 34);
						char temp_str1[50] = "";
						strncpy(temp_str1, substr_start+2,  substr_len-2);
						int temp_sum1 = toTen(temp_str1, 16);
						sprintf(tokens[nr_token].str, "%d", temp_sum1);
						nr_token++;
						break;
					case '|': tokens[nr_token++].type = '|';break;
					case '&': tokens[nr_token++].type = '&';break;
					default: panic("don't implement now\n");
				}
				if(nr_token>=50)
				{
					printf("out of range\n");
					return false;
				}

				break;
			}
		}
	

		if(i == NR_REGEX) {
			printf("no match at position %d\n%s\n%*.s^\n", position, e, position, "");
			return false;
		}
	}

	return true; 
}

int check_parentheses(int p,  int q)
{
	if(tokens[p].type != '(' || tokens[q].type != ')')
	{
		return 0;
	}
	int sum = 0;
	int now_flag = 0;
	int i1 = 0 ;
		while(i1 < 100){
			i1++;
		}
	int i=0;
	for(i=p; i<=q; i++)
	{
		if(tokens[i].type == '(')
		{
			sum++;
		}
		if(tokens[i].type == ')')
		{
			sum--;
		}
		if(sum == 0)
		{
			now_flag = i;
			break;
		}
	}
	if(now_flag == q && sum == 0)
	{
		return 1;
	}
	else
	{
		return 0;
	}
	
}


int eval(int p, int q)
{
	//假设加减优先级是１，乘除优先级是２
	//先提取出来的是优先级低的，括号优先级最高所以最晚提取
	//p is the left part
	int now = 0;
	if (p > q)
	{
		assert(0);
	}
	else if(p == q)
	{
		if(tokens[p].type == NUM)
		{
			return toTen(tokens[p].str, 10);
		}
	}
	else if(check_parentheses(p, q) == 1)
	{
		return eval(p+1, q-1);
	}
	else
	{
		int op=0;
		int brakets_sum = 0;
		int priority = 100;
		int i1 = 0 ;
		while(i1 < 100){
			i1++;
		}
		int now_priority = 0;
		int i=0;
		for(i=p; i<=q; i++)
		{
			if(tokens[i].type == NUM)
			{
				continue;
			}
			if(tokens[i].type == '(')
			{
				brakets_sum++;
			}
			if(tokens[i].type == ')')
			{
				brakets_sum--;
			}
			switch(tokens[i].type)
			{
				case '|': now_priority = 1;break;
				case '&': now_priority = 1;break;
				case EQ: now_priority = 2;break;
				case NQ: now_priority = 2; break;
				case '+': now_priority = 3;break;
				case '-': now_priority = 3; break;
				case '*': now_priority = 4;break;
				case '/': now_priority = 4;break;
				case '!': now_priority = 5;break;
				case NEG: now_priority = 5;break;
				case DEREF: now_priority=5;break;
			}
			if(brakets_sum == 0 && now_priority <= priority && tokens[i].type != ')')
			{
				op = tokens[i].type;
				priority = now_priority;
				now = i;
			}
		}
		int val1 = 0;
		if(priority != 5)
		{
			val1 = eval(p, now - 1);
		}
		int val2 = eval(now+1, q);

		switch (op)
		{
		case '+':  return val1 + val2;
			break;
	        case '-': return val1 - val2;
		break;
		case '*': return val1 * val2;
		break;
		case '/':return val1 / val2; break;
		case '!': return val2 == 0 ? 1 : 0;break;
		case '|': 
	        	if(val1 != 0 || val2 != 0)
			{
				return 1;
			}
			else{return 0;}
			break;
		case '&':
			if(val1 != 0 && val2 != 0)
			{
				return 1;
			}
			else
			{
				return 0;
			}
			break;
		case EQ: return val1 == val2;break;
		case NQ: return val1 != val2;break;
		case NEG: return -val2;break;
			
		
		default:
			break;
		}
	}
	return 0;
}

uint32_t expr(char *e, bool *success) {
	if(!make_token(e)) {
		*success = false;
		return 0;
	}
	int sum = 0;
	int i=0;
	for(i=0; i<nr_token; i++)
	{
		if(tokens[i].type == '(')
		{
			sum++;
		}
		if(tokens[i].type == ')')
		{
			sum--;
		}
		if(sum == 0)
		{
			break;	
		}
	}
	if(sum != 0)
	{
		printf("brakets match failure\n");
		*success =false;
		return -1;
	}
	/* TODO: Insert codes to evaluate the expression. */
	*success = true;
	return eval(0, nr_token-1);
}